<template>
  <div class="fa-contract-toolbar">
    <div class="fa-contract-toolbar__left">
      <slot name="left" />
    </div>

    <div class="pagination">
      <a class="pagination-btn" :class="{ 'is-disabled': page <= 1 }" @click="page--">
        <i class="pagination-btn-icon el-icon-arrow-left"></i>
        上一页
      </a>

      <ElDropdown
        class="page-counter-wrapper"
        size="mini"
        placement="top"
        :disabled="pageEdit.isFocus"
        @click.native="pageEdit.isFocus = true"
      >
        <div class="page-counter" :class="{ 'is-focus': pageEdit.isFocus }">
          <span
            v-if="pageEdit.isFocus"
            key="input"
            ref="pageInput"
            class="current"
            :contenteditable="contenteditable"
            @focus="pageEdit.onFocus"
            @blur="pageEdit.submit"
            @input="pageEdit.onInput"
            @keyup.esc="pageEdit.cancel"
            @keyup.enter="pageEdit.confirm"
          ></span>
          <span v-else key="text" class="current">{{ page }}</span>
          /
          {{ pageCount }}
        </div>

        <ElDropdownMenu
          slot="dropdown"
          class="fa-contract-toolbar__dropdown page-dropdown"
          :class="{ overflow: pageCount > 10 }"
        >
          <ElDropdownItem v-for="p in pages" :key="p" :class="{ 'is-active': p === page }" @click.native="page = p">
            {{ p }}
          </ElDropdownItem>
        </ElDropdownMenu>
      </ElDropdown>

      <a class="pagination-btn" :class="{ 'is-disabled': page >= pageCount }" @click="page++">
        下一页
        <i class="pagination-btn-icon el-icon-arrow-right"></i>
      </a>
    </div>

    <div class="fa-contract-toolbar__right">
      <a
        class="zoom-btn el-icon-minus"
        :class="{ 'is-disabled': zoom.level <= zoom.min }"
        @click="zoom.level -= zoomStep"
      ></a>

      <ElDropdown class="zoom-select" size="mini" placement="top">
        <div class="zoom-select__value">
          {{ format(zoom.level) }}
        </div>

        <ElDropdownMenu slot="dropdown" class="fa-contract-toolbar__dropdown">
          <ElDropdownItem
            v-for="{ value, text } in zoomLevels"
            :key="value"
            :class="{ 'is-active': value.toFixed(2) === zoom.level.toFixed(2) }"
            @click.native="zoom.level = value"
          >
            {{ text }}
          </ElDropdownItem>
        </ElDropdownMenu>
      </ElDropdown>

      <a
        class="zoom-btn el-icon-plus"
        :class="{ 'is-disabled': zoom.level >= zoom.max }"
        @click="zoom.level += zoomStep"
      ></a>

      <i class="split"></i>

      <ElTooltip v-if="zoom.level !== 1" content="原始宽度">
        <a
          class="adjust-btn fa-icon-origin-width"
          :class="{ 'is-active': zoom.level === 1 }"
          @click="zoom.level = 1"
        ></a>
      </ElTooltip>

      <ElTooltip v-else content="适应宽度">
        <a
          class="adjust-btn fa-icon-adapt-width"
          :class="{ 'is-active': zoom.level === adapt }"
          @click="zoom.level = adapt"
        ></a>
      </ElTooltip>

      <slot name="right" />
    </div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, PropType, inject, nextTick, reactive, ref, toRefs, watch } from 'vue'
import { Viewer } from '@/types'
import useZoom from './useZoom'

/** 数字转百分比 */
const format = (val: number) => (val * 100).toFixed(0) + '%'

/** 页码输入 */
function usePageEdit(onChange: (val: string, input: HTMLInputElement) => void) {
  const input = ref(null as null | HTMLInputElement)

  const isFocus = ref(false)

  watch(isFocus, async focused => {
    await nextTick()
    focused && input.value?.focus()
  })

  /** 提交 */
  function submit() {
    isFocus.value = false
    const ipt = input.value
    ipt && onChange(ipt.textContent || '', ipt)
  }

  return reactive({
    input,

    /** 是否聚焦 */
    isFocus,

    /** 提交 */
    submit,

    /** 聚焦 */
    onFocus: () => {
      const el = input.value

      isFocus.value = true

      if (!el) return
    },

    /** 输入 */
    onInput() {
      const el = input.value
      if (!el) return
      el.textContent = (el.textContent || '').replace(/\D+/g, '')
    },

    /** 取消 */
    cancel() {
      isFocus.value = false
    },

    /** 确认 */
    confirm() {
      submit()
    }
  })
}

export default defineComponent({
  name: 'FaContractToolbar',

  props: {
    /** 页面索引，.sync 双向绑定 */
    pageIndex: {
      type: Number,
      default: 0
    },

    /** 总页数 */
    pageCount: {
      type: Number,
      default: 0
    },

    /** 缩放 */
    zoom: {
      type: Object as PropType<ReturnType<typeof useZoom>>,
      default: 1
    },

    /** 缩放级别列表 */
    levels: {
      type: Array as PropType<number[]>,
      default: []
    },

    /** 缩放步长 */
    zoomStep: {
      type: Number,
      default: 0.1
    }
  },

  setup(props, { emit }) {
    const viewer = inject('Viewer') as Viewer

    /** 页码 */
    const page = computed({
      set(val) {
        const count = props.pageCount

        if (val > count) {
          val = count
        } else if (val < 1) {
          val = 1
        }

        viewer.scroll.scrollToPage(val - 1)
      },
      get: () => props.pageIndex + 1
    })

    /** 页码输入 */
    const pageEdit = usePageEdit(async raw => {
      const p = +raw

      if (p > 0 && p <= props.pageCount) {
        page.value = p
      }
    })

    const { input: pageInput } = toRefs(pageEdit)

    return {
      pageEdit,

      pageInput,

      /** 页码 */
      page,

      /** 页码选择列表 */
      pages: computed(() => {
        /** 最大页面数量 */
        const max = 100
        const count = props.pageCount
        const p = page.value

        // vue 可直接遍历数字，但遍历里 ts 类型会识别错误
        if (count < max + 1) return count as unknown as number[]

        const start = Math.floor((p - 1) / max) * max + 1
        const end = Math.min(count, start + max - 1)

        const list = Array.from(new Array(end - start + 1), (val, index) => start + index)

        if (start !== 1) list.unshift(1)

        if (end !== count) list.push(count)

        return list
      }),

      /** 缩放级别列表 */
      zoomLevels: computed(() =>
        props.levels.map(value => ({
          value,
          text: format(value)
        }))
      ),

      /** 页码是否可编辑 */
      contenteditable: 'plaintext-only' as unknown as boolean,

      /** 适应宽度 */
      adapt: computed(() => (viewer.scroll.state.width - viewer.pageSpacing * 2) / viewer.layout.docAreaWidth),

      /** 格式化为百分比 */
      format
    }
  }
})
</script>

<style lang="scss">
.fa-contract-toolbar {
  position: relative;
  display: flex;
  flex: none;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
  height: $contract-toolbar-height;
  padding: 0 16px;
  font-size: 12px;
  background-color: $background-color-secondary;
  box-shadow: 0 -2px 4px 0 rgba(227, 230, 233, 0.5);

  .link {
    color: $color-text-regular;
    cursor: pointer;
    transition: all 0.2s ease-in-out;
    user-select: none;

    &:hover {
      color: $color-primary;
    }

    &:active {
      color: $color-primary-darker;
    }

    &.is-disabled {
      color: $color-text-disabled;
      cursor: default;
    }
  }

  .btn {
    height: 22px;
    color: $color-text-secondary;
    line-height: 22px;
    text-align: center;
    border-radius: 1px;
    cursor: pointer;
    transition: all 0.2s ease-in-out;
    user-select: nonet;

    &:hover,
    &:active {
      background-color: $background-color-disabled;
    }

    &:active {
      color: #555;
      background-color: #ddd;
    }
  }

  .pagination {
    position: absolute;
    top: 0;
    left: 50%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 100%;
    transform: translateX(-50%);

    .pagination-btn {
      @extend .link;
      line-height: 28px;

      &-icon {
        font-size: 12px;
        line-height: 1.5;
        vertical-align: middle;
      }
    }
  }

  .page-counter {
    box-sizing: border-box;
    min-width: 60px;
    height: 22px;
    margin: 0 8px;
    padding: 0 4px;
    color: $color-text-regular;
    font-size: 12px;
    line-height: 20px;
    text-align: center;
    border: 1px solid transparent;
    cursor: pointer;

    &.is-focus,
    &:hover {
      border: $border-solid;
    }

    &:hover {
      background: rgba(#fff, 0.5);
    }

    &.is-focus {
      color: $color-text-disabled;
      background: #fff;
      border-radius: 1px;
      cursor: pointer;

      .current {
        color: $color-text-regular;
      }
    }

    .current {
      display: inline-block;
      min-width: 1em;

      &:focus {
        outline: none;
      }
    }
  }

  &__dropdown {
    &.page-dropdown {
      min-width: 40px;

      &.overflow {
        max-height: 300px;
        overflow: auto;
      }
    }

    .el-dropdown-menu__item {
      justify-content: center;
      height: 30px;
      line-height: 30px;
      text-align: center;

      &.is-active {
        color: $color-primary;
      }
    }
  }

  &__right {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .zoom-btn {
    $size: 20px;
    flex: none;
    width: $size;
    height: $size;
    color: $color-text-secondary;
    font-size: 12px;
    line-height: $size;
    text-align: center;

    @extend .link;
  }

  .zoom-select {
    &__value {
      width: 50px;
      font-size: 12px;
      @extend .btn;
    }
  }

  .split {
    width: 1px;
    height: 16px;
    margin: 0 8px;
    line-height: 1;
    background-color: $border-color;
  }

  .adjust-btn {
    @extend .btn;
    width: 22px;
    font-size: 17px;
    background-color: transparent;
  }
}
</style>
